Įvaldykite Scikit-learn Pipeline, kad supaprastintumėte ML darbo eigas. Automatizuokite apdorojimą, modelių mokymą ir hiperparametrų derinimą patikimiems, atkuriamiems modeliams.
Scikit-learn Pipeline: Išsamus vadovas ML darbo eigos automatizavimui
Mašininio mokymosi pasaulyje modelio kūrimas dažnai vaizduojamas kaip įspūdingas galutinis žingsnis. Tačiau patyrę duomenų mokslininkai ir ML inžinieriai žino, kad kelias į patikimą modelį yra grindžiamas daugybe esminių, dažnai pasikartojančių ir klaidoms imlių žingsnių: duomenų valymu, požymių mastelio keitimu, kategorinių kintamųjų kodavimu ir kt. Šių žingsnių valdymas atskirai mokymo, patvirtinimo ir testavimo rinkiniams gali greitai tapti logistiniu košmaru, vedančiu į subtilias klaidas ir, pavojingiausia, duomenų nutekėjimą.
Štai kur „Scikit-learn“ „Pipeline“ ateina į pagalbą. Tai ne tik patogumas; tai yra pagrindinė priemonė profesionalioms, atkuriamoms ir gamybai paruoštoms mašininio mokymosi sistemoms kurti. Šis išsamus vadovas supažindins jus su viskuo, ką reikia žinoti, norint įvaldyti „Scikit-learn“ „Pipelines“, nuo pagrindinių sąvokų iki pažangių metodų.
Problema: Rankinė mašininio mokymosi darbo eiga
Apsvarstykime tipinę mokymosi su mokytoju užduotį. Prieš iškviesdami model.fit(), turite paruošti duomenis. Standartinė darbo eiga gali atrodyti taip:
- Padalinti duomenis: Padalinkite savo duomenų rinkinį į mokymo ir testavimo rinkinius. Tai yra pirmasis ir svarbiausias žingsnis siekiant užtikrinti, kad galėtumėte įvertinti savo modelio veikimą su nematomiems duomenims.
- Tvarkyti trūkstamas reikšmes: Nustatykite ir užpildykite trūkstamus duomenis mokymo rinkinyje (pvz., naudojant vidurkį, medianą arba konstantą).
- Koduoti kategorinius požymius: Konvertuokite ne skaitmeninius stulpelius, pvz., „Šalis“ ar „Produkto kategorija“, į skaitmeninį formatą, naudodami tokias technikas kaip „One-Hot Encoding“ arba „Ordinal Encoding“.
- Mastelio keitimas skaitmeniniams požymiams: Visus skaitmeninius požymius pritaikykite panašiam masteliui, naudodami tokius metodus kaip standartizavimas (
StandardScaler) arba normalizavimas (MinMaxScaler). Tai yra labai svarbu daugeliui algoritmų, tokių kaip SVM, logistinė regresija ir neuroniniai tinklai. - Apmokyti modelį: Galiausiai, apmokykite pasirinktą mašininio mokymosi modelį su iš anksto apdorotais mokymo duomenimis.
Dabar, kai norite atlikti prognozes su testavimo rinkiniu (arba naujais, nematomiems duomenimis), turite pakartoti lygiai tuos pačius išankstinio apdorojimo žingsnius. Turite taikyti tą pačią užpildymo strategiją (naudodami reikšmę, apskaičiuotą iš mokymo rinkinio), tą pačią kodavimo schemą ir tuos pačius mastelio keitimo parametrus. Rankinis visų šių apmokytų transformatorių sekimas yra nuobodus ir yra pagrindinis klaidų šaltinis.
Didžiausia rizika čia yra duomenų nutekėjimas. Tai įvyksta, kai informacija iš testavimo rinkinio netyčia patenka į mokymo procesą. Pavyzdžiui, jei apskaičiuojate vidurkį trūkstamoms reikšmėms užpildyti arba mastelio keitimo parametrus iš viso duomenų rinkinio prieš padalijimą, jūsų modelis netiesiogiai mokosi iš testavimo duomenų. Tai veda prie pernelyg optimistinio veikimo įvertinimo ir modelio, kuris realiuoju pasauliu veikia apgailėtinai.
Pristatome Scikit-learn „Pipelines“: Automatizuotas sprendimas
„Scikit-learn“ Pipeline yra objektas, kuris sujungia kelis duomenų transformavimo žingsnius ir galutinį įvertintoją (pvz., klasifikatorių ar regresorių) į vieną, bendrą objektą. Galite įsivaizduoti tai kaip surinkimo liniją savo duomenims.
Kai iškviečiate .fit() ant „Pipeline“, jis nuosekliai taiko fit_transform() kiekvienam tarpiniam žingsniui su mokymo duomenimis, perduodamas vieno žingsnio išvestį kaip įvestį kitam. Galiausiai, jis iškviečia .fit() ant paskutinio žingsnio – įvertintojo. Kai iškviečiate .predict() arba .transform() ant „Pipeline“, jis taiko tik .transform() metodą kiekvienam tarpiniam žingsniui su naujais duomenimis prieš atlikdamas prognozę su galutiniu įvertintoju.
Pagrindiniai „Pipelines“ naudojimo privalumai
- Duomenų nutekėjimo prevencija: Tai yra pats svarbiausias privalumas. Įtraukdami visą išankstinį apdorojimą į „Pipeline“, užtikrinate, kad transformacijos būtų išmokstamos tik iš mokymo duomenų kryžminio patvirtinimo metu ir teisingai taikomos patvirtinimo/testavimo duomenims.
- Paprastumas ir organizuotumas: Visa jūsų darbo eiga, nuo neapdorotų duomenų iki apmokyto modelio, yra kondensuojama į vieną objektą. Tai daro jūsų kodą švaresnį, lengviau skaitomą ir lengviau valdomą.
- Atkuriamumas: „Pipeline“ objektas apima visą jūsų modeliavimo procesą. Galite lengvai išsaugoti šį vieną objektą (pvz., naudodami `joblib` arba `pickle`) ir vėliau įkelti jį prognozėms atlikti, užtikrindami, kad kiekvieną kartą būtų laikomasi tų pačių žingsnių.
- Efektyvumas „Grid Search“ metu: Galite atlikti hiperparametrų derinimą per visą „Pipeline“ iš karto, vienu metu randant geriausius parametrus tiek išankstinio apdorojimo žingsniams, tiek galutiniam modeliui. Šią galingą funkciją aptarsime vėliau.
Kuriame pirmąjį paprastą „Pipeline“
Pradėkime nuo paprasto pavyzdžio. Įsivaizduokite, kad turime skaitmeninį duomenų rinkinį ir norime pakeisti duomenų mastelį prieš apmokant logistinės regresijos modelį. Štai kaip sukurtumėte „Pipeline“ tam.
Pirmiausia, nustatykime savo aplinką ir sukurkime keletą pavyzdinių duomenų.
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from sklearn.metrics import accuracy_score
# Generate some sample data
X, y = np.random.rand(100, 5) * 10, (np.random.rand(100) > 0.5).astype(int)
# Split data into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
Dabar, apibrėžkime savo „Pipeline“. „Pipeline“ sukuriama pateikiant žingsnių sąrašą. Kiekvienas žingsnis yra kortelė, turinti pavadinimą (jūsų pasirinktą eilutę) ir patį transformatoriaus ar įvertintojo objektą.
# Create the pipeline steps
steps = [
('scaler', StandardScaler()),
('classifier', LogisticRegression())
]
# Create the Pipeline object
pipe = Pipeline(steps)
# Now, you can treat the 'pipe' object as if it were a regular model.
# Let's train it on our training data.
pipe.fit(X_train, y_train)
# Make predictions on the test data
y_pred = pipe.predict(X_test)
# Evaluate the model
accuracy = accuracy_score(y_test, y_pred)
print(f"Pipeline Accuracy: {accuracy:.4f}")
Štai ir viskas! Vos per kelias eilutes sujungėme mastelio keitimą ir klasifikaciją. „Scikit-learn“ tvarko visą tarpinę logiką. Kai iškviečiamas pipe.fit(X_train, y_train), jis pirmiausia iškviečia StandardScaler().fit_transform(X_train) ir tada perduoda rezultatą LogisticRegression().fit(). Kai iškviečiamas pipe.predict(X_test), jis taiko jau apmokytą mastelio keitiklį, naudodamas StandardScaler().transform(X_test), prieš darydamas prognozes su logistinės regresijos modeliu.
Darbas su nevienalydžiais duomenimis: „ColumnTransformer“
Realaus pasaulio duomenų rinkiniai retai būna paprasti. Juose dažnai yra įvairių duomenų tipų: skaitmeninių stulpelių, kuriems reikia mastelio keitimo, kategorinių stulpelių, kuriems reikia kodavimo, ir galbūt teksto stulpelių, kuriems reikia vektorizavimo. Paprastas nuoseklus „Pipeline“ tam nepakanka, nes skirtingiems stulpeliams reikia taikyti skirtingas transformacijas.
Štai kur švyti ColumnTransformer. Jis leidžia taikyti skirtingus transformatorius skirtingiems jūsų duomenų stulpelių pogrupiams ir tada sumaniai sujungia rezultatus. Tai puikus įrankis, skirtas naudoti kaip išankstinio apdorojimo žingsnis didesniame „Pipeline“.
Pavyzdys: Skaitmeninių ir kategorinių požymių derinimas
Sukurkime realistiškesnį duomenų rinkinį su skaitmeniniais ir kategoriniais požymiais, naudodami „pandas“.
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder
from sklearn.impute import SimpleImputer
# Create a sample DataFrame
data = {
'age': [25, 30, 45, 35, 50, np.nan, 22],
'salary': [50000, 60000, 120000, 80000, 150000, 75000, 45000],
'country': ['USA', 'Canada', 'USA', 'UK', 'Canada', 'USA', 'UK'],
'purchased': [0, 1, 1, 0, 1, 1, 0]
}
df = pd.DataFrame(data)
X = df.drop('purchased', axis=1)
y = df['purchased']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Identify numerical and categorical columns
numerical_features = ['age', 'salary']
categorical_features = ['country']
Mūsų išankstinio apdorojimo strategija bus tokia:
- Skaitmeniniams stulpeliams (
age,salary): Trūkstamas reikšmes užpildykite medianos reikšme, tada pakeiskite jų mastelį. - Kategoriniams stulpeliams (
country): Trūkstamas reikšmes užpildykite dažniausiai pasitaikančia kategorija, tada atlikite „one-hot“ kodavimą.
Šiuos žingsnius galime apibrėžti naudodami du atskirus mini „Pipelines“.
# Create a pipeline for numerical features
numeric_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler())
])
# Create a pipeline for categorical features
categorical_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='most_frequent')),
('onehot', OneHotEncoder(handle_unknown='ignore'))
])
Dabar, naudojame `ColumnTransformer`, kad pritaikytume šiuos „Pipelines“ teisingiems stulpeliams.
# Create the preprocessor with ColumnTransformer
preprocessor = ColumnTransformer(
transformers=[
('num', numeric_transformer, numerical_features),
('cat', categorical_transformer, categorical_features)
])
ColumnTransformer priima transformers sąrašą. Kiekvienas transformatorius yra kortelė, turinti pavadinimą, transformatoriaus objektą (kuris pats gali būti „Pipeline“) ir stulpelių pavadinimų sąrašą, kuriems jį reikia taikyti.
Galiausiai, galime įdėti šį preprocessor kaip pirmąjį žingsnį mūsų pagrindiniame „Pipeline“, po kurio seka mūsų galutinis įvertintojas.
from sklearn.ensemble import RandomForestClassifier
# Create the full pipeline
full_pipeline = Pipeline(steps=[
('preprocessor', preprocessor),
('classifier', RandomForestClassifier(random_state=42))
])
# Train and evaluate the full pipeline
full_pipeline.fit(X_train, y_train)
print("Model score on test data:", full_pipeline.score(X_test, y_test))
# You can now make predictions on new raw data
new_data = pd.DataFrame({
'age': [40, 28],
'salary': [90000, 55000],
'country': ['USA', 'Germany'] # 'Germany' is an unknown category
})
predictions = full_pipeline.predict(new_data)
print("Predictions for new data:", predictions)
Atkreipkite dėmesį, kaip elegantiškai tai tvarko sudėtingą darbo eigą. Parametras handle_unknown='ignore' OneHotEncoder ypač naudingas gamybos sistemoms, nes jis apsaugo nuo klaidų, kai duomenyse pasirodo naujos, nematytos kategorijos.
Pažangios „Pipeline“ technikos
„Pipelines“ suteikia dar daugiau galios ir lankstumo. Panagrinėkime keletą pažangių funkcijų, kurios yra būtinos profesionaliems mašininio mokymosi projektams.
Savo transformatorių kūrimas
Kartais įmontuotų „Scikit-learn“ transformatorių nepakanka. Gali prireikti atlikti konkrečiai sričiai skirtą transformaciją, pvz., išgauti požymio logaritmą arba sujungti du požymius į naują. Galite lengvai sukurti savo pasirinktinius transformatorius, kurie sklandžiai integruojami į „Pipeline“.
Norėdami tai padaryti, sukuriate klasę, kuri paveldima iš `BaseEstimator` ir `TransformerMixin`. Jums reikia įdiegti tik `fit()` ir `transform()` metodus (ir `__init__()`, jei reikia).
Sukurkime transformatorių, kuris prideda naują požymį: atlyginimo ir amžiaus santykį.
from sklearn.base import BaseEstimator, TransformerMixin
# Define column indices (can also pass names)
age_ix, salary_ix = 0, 1
class FeatureRatioAdder(BaseEstimator, TransformerMixin):
def __init__(self):
pass # No parameters to set
def fit(self, X, y=None):
return self # Nothing to learn during fit, so just return self
def transform(self, X):
salary_age_ratio = X[:, salary_ix] / X[:, age_ix]
return np.c_[X, salary_age_ratio] # Concatenate original X with new feature
Tada galėtumėte įterpti šį pasirinktinį transformatorių į savo skaitmeninio apdorojimo „Pipeline“:
numeric_transformer_with_custom = Pipeline(steps=[
('imputer', SimpleImputer(strategy='median')),
('ratio_adder', FeatureRatioAdder()), # Our custom transformer
('scaler', StandardScaler())
])
Šis tinkinimo lygis leidžia jums įtraukti visą savo požymių inžinerijos logiką į „Pipeline“, todėl jūsų darbo eiga tampa itin mobili ir atkuriamoji.
Hiperparametrų derinimas su „Pipelines“ naudojant `GridSearchCV`
Tai neabejotinai yra viena iš galingiausių „Pipelines“ programų. Galite vienu metu ieškoti geriausių hiperparametrų visai savo darbo eigai, įskaitant išankstinio apdorojimo žingsnius ir galutinį modelį.
Norėdami nurodyti, kuriuos parametrus derinti, naudokite specialią sintaksę: `step_name__parameter_name`.
Išplėskime mūsų ankstesnį pavyzdį ir sureguliuokime tiek mūsų išankstinio apdorojimo priemonės (imputer), tiek RandomForestClassifier hiperparametrus.
from sklearn.model_selection import GridSearchCV
# We use the 'full_pipeline' from the ColumnTransformer example
# Define the parameter grid
param_grid = {
'preprocessor__num__imputer__strategy': ['mean', 'median'],
'classifier__n_estimators': [50, 100, 200],
'classifier__max_depth': [None, 10, 20],
'classifier__min_samples_leaf': [1, 2, 4]
}
# Create the GridSearchCV object
grid_search = GridSearchCV(full_pipeline, param_grid, cv=5, verbose=1, n_jobs=-1)
# Fit it to the data
grid_search.fit(X_train, y_train)
# Print the best parameters and score
print("Best parameters found: ", grid_search.best_params_)
print("Best cross-validation score: ", grid_search.best_score_)
# The best estimator is already refitted on the whole training data
best_model = grid_search.best_estimator_
print("Test set score with best model: ", best_model.score(X_test, y_test))
Atidžiai pažvelkite į `param_grid` raktus:
'preprocessor__num__imputer__strategy': Tai nukreipta į `SimpleImputer` žingsnio, pavadinto `imputer`, parametrą `strategy`, kuris yra skaitmeniniame „Pipeline“, pavadintame `num`, o pastarasis yra `ColumnTransformer` objekte, pavadintame `preprocessor`.'classifier__n_estimators': Tai nukreipta į galutinio įvertintojo, pavadinto `classifier`, parametrą `n_estimators`.
Taip darydamas, `GridSearchCV` teisingai išbando visus derinius ir randa optimalų parametrų rinkinį visai darbo eigai, visiškai užkertant kelią duomenų nutekėjimui derinimo proceso metu, nes visas išankstinis apdorojimas atliekamas kiekviename kryžminio patvirtinimo segmente.
„Pipeline“ vizualizavimas ir tikrinimas
Sudėtingus „Pipelines“ gali būti sunku suprasti. „Scikit-learn“ siūlo puikų būdą juos vizualizuoti. Nuo 0.23 versijos galite gauti interaktyvų HTML atvaizdą.
from sklearn import set_config
# Set display to 'diagram' to get the visual representation
set_config(display='diagram')
# Now, simply displaying the pipeline object in a Jupyter Notebook or similar environment will render it
full_pipeline
Tai sugeneruos diagramą, kuri parodo duomenų srautą per kiekvieną transformatorių ir įvertintoją, kartu su jų pavadinimais. Tai nepaprastai naudinga derinimui, jūsų darbo dalijimuisi ir modelio struktūros supratimui.
Taip pat galite pasiekti atskirus apmokyto „Pipeline“ žingsnius, naudodami jų pavadinimus:
# Access the final classifier of the fitted pipeline
final_classifier = full_pipeline.named_steps['classifier']
print("Feature importances:", final_classifier.feature_importances_)
# Access the OneHotEncoder to see the learned categories
onehot_encoder = full_pipeline.named_steps['preprocessor'].named_transformers_['cat'].named_steps['onehot']
print("Categorical features learned:", onehot_encoder.categories_)
Dažnos klaidos ir geriausia praktika
- Apmokymas su neteisingais duomenimis: Visada, visada apmokykite savo „Pipeline“ TIK su mokymo duomenimis. Niekada neapmokykite jo su visu duomenų rinkiniu ar testavimo rinkiniu. Tai yra pagrindinė taisyklė, siekiant išvengti duomenų nutekėjimo.
- Duomenų formatai: Atkreipkite dėmesį į duomenų formatą, kurio tikisi kiekvienas žingsnis. Kai kurie transformatoriai (kaip ir mūsų pasirinktiniame pavyzdyje) gali dirbti su „NumPy“ masyvais, o kiti patogesni su „Pandas DataFrames“. „Scikit-learn“ paprastai gerai tvarko tai, tačiau reikia atkreipti dėmesį, ypač su pasirinktiniais transformatoriais.
- „Pipelines“ išsaugojimas ir įkėlimas: Norėdami diegti savo modelį, turėsite išsaugoti apmokytą „Pipeline“. Standartinis būdas tai padaryti „Python“ ekosistemoje yra su `joblib` arba `pickle`. `joblib` dažnai efektyvesnis objektams, kurie turi didelius „NumPy“ masyvus.
import joblib # Save the pipeline joblib.dump(full_pipeline, 'my_model_pipeline.joblib') # Load the pipeline later loaded_pipeline = joblib.load('my_model_pipeline.joblib') # Make predictions with the loaded model loaded_pipeline.predict(new_data) - Naudokite aprašomuosius pavadinimus: Suteikite savo „Pipeline“ žingsniams ir `ColumnTransformer` komponentams aiškius, aprašomuosius pavadinimus (pvz., 'numeric_imputer', 'categorical_encoder', 'svm_classifier'). Tai daro jūsų kodą lengviau skaitomą ir supaprastina hiperparametrų derinimą bei derinimą.
Išvada: Kodėl „Pipelines“ yra neatsiejami profesionaliame ML
„Scikit-learn Pipelines“ yra ne tik įrankis tvarkingesniam kodui rašyti; jie atspindi paradigmos pokytį nuo rankinio, klaidoms imlaus programavimo iki sistemingo, patikimo ir atkuriamo požiūrio į mašininį mokymąsi. Jie yra patikimos ML inžinerijos praktikos pagrindas.
Naudodami „Pipelines“, jūs gaunate:
- Patikimumas: Pašalinate dažniausią klaidų šaltinį mašininio mokymosi projektuose – duomenų nutekėjimą.
- Efektyvumas: Supaprastinate visą savo darbo eigą, nuo požymių inžinerijos iki hiperparametrų derinimo, į vieną, vientisą vienetą.
- Atkuriamumas: Sukuriate vieną, serializuojamą objektą, kuriame yra visa jūsų modelio logika, todėl jį lengva diegti ir dalintis.
Jei rimtai ketinate kurti mašininio mokymosi modelius, kurie patikimai veikia realiame pasaulyje, „Scikit-learn Pipelines“ įvaldymas nėra pasirinktinis – jis būtinas. Pradėkite juos įtraukti į savo projektus jau šiandien, ir jūs kursite geresnius, patikimesnius modelius greičiau nei bet kada anksčiau.